package cc.unitmesh.pick.worker.base

import cc.unitmesh.core.completion.TypedIns
import cc.unitmesh.pick.project.ProjectContext
import cc.unitmesh.pick.worker.WorkerContext
import cc.unitmesh.pick.worker.job.InstructionFileJob
import cc.unitmesh.pick.worker.job.JobContext
import kotlinx.coroutines.coroutineScope
import org.slf4j.Logger
import java.io.File

/**
 * The `LangWorker` interface represents a worker responsible for preparing and executing instruction file jobs.
 *
 * It provides access to the context, file tree, logger, and list of jobs.
 * The `prepareJob` method is used to prepare a given instruction file job for execution.
 *
 * The `start` method is responsible for starting the execution of the job processing. It processes each job in the `jobs` list
 * and writes the output to a file specified in the `pureDataFileName` property of the `context`.
 *
 * @property workerContext The worker context associated with the LangWorker.
 * @property fileTree A hashmap representing the file tree associated with the LangWorker.
 * @property logger The logger to be used for logging.
 * @property jobs A mutable list of instruction file jobs to be processed.
 *
 * @see WorkerContext
 * @see InstructionFileJob
 * @see Logger
 * @see TypedIns
 */
interface LangWorker {
    val workerContext: WorkerContext
    val fileTree: HashMap<String, InstructionFileJob>
    val logger: Logger
    val jobs: MutableList<InstructionFileJob>

    /**
     * Prepares the given instruction file job for execution.
     *
     * @param job The instruction file job to be prepared.
     */
    fun prepareJob(job: InstructionFileJob)

    /**
     * Tries to add a class to the file tree.
     *
     * This method is used to add a class to the file tree based on the information provided by the `InstructionFileJob` object.
     * The file tree is a data structure used to store classes, where the key is the full class name which consists of
     * the package name and the class name.
     *
     * In Java, Kotlin, and Scala, the key for the file tree is the full class name.
     * In Typescript, the path import is converted to the full class name.
     *
     * @param job the InstructionFileJob object representing the class to be added to the file tree
     */
    fun tryAddClassToTree(job: InstructionFileJob) {}

    /**
     * Starts the execution of the job processing.
     *
     * This method processes each job in the `jobs` list and writes the output to a file specified in the
     * `pureDataFileName` property of the `context`.
     *
     * If the file does not exist, it will be created. If there are any errors while creating the file,
     * an error message will be logged and an empty list will be returned.
     *
     * The method uses a coroutine scope for suspending the execution and allows concurrent processing of jobs.
     *
     * @return a list of `TypedIns` objects representing the output of the job processing. An empty list
     * will be returned if there are any errors.
     */
    suspend fun start(): List<TypedIns> = coroutineScope {
        val outputFile = File(workerContext.pureDataFileName)
        if (!outputFile.exists()) {
            try {
                outputFile.createNewFile()
            } catch (e: Exception) {
                logger.error("create file error: $outputFile")
                e.printStackTrace()
                return@coroutineScope emptyList()
            }
        }

        val result = executeJob().map {
            outputFile.appendText(it.toString() + "\n")
            it
        }

        return@coroutineScope result
    }

    /**
     * Executes a job and returns a list of typed instructions.
     *
     * This method iterates over the list of jobs and creates a job context for each job using the provided worker context.
     * It then iterates over the code context strategies of the worker context and builds a list of typed instructions
     * using the job context and each code context strategy. The resulting list of typed instructions is flattened and returned.
     *
     * @return a list of typed instructions generated by executing the jobs
     */
    fun executeJob(): List<TypedIns> {
        return jobs.map { job ->
            val jobContext = createJob(job, workerContext)

            workerContext.codeContextStrategies.map { type ->
                type.builder(jobContext).build()
            }.flatten()
        }.flatten()
    }

    /**
     * Creates a JobContext object based on the given InstructionFileJob and WorkerContext.
     *
     * @param job The InstructionFileJob object representing the job to be processed.
     * @param workerContext The WorkerContext object containing the context information for the worker.
     * @return A JobContext object containing the job, quality types, file tree, output configuration, completion types,
     *         maximum completion in one file, project context, and quality threshold.
     */
    fun createJob(job: InstructionFileJob, workerContext: WorkerContext) = JobContext(
        job,
        workerContext.qualityTypes,
        fileTree,
        workerContext.insOutputConfig,
        workerContext.completionTypes,
        workerContext.maxCompletionInOneFile,
        project = ProjectContext(
            language = workerContext.project.language,
            compositionDependency = workerContext.compositionDependency,
        ),
        workerContext.qualityThreshold
    )

}